package com.example.redislearning.service.impl;

import com.example.redislearning.entity.User;
import com.example.redislearning.mapper.UserMapper;
import com.example.redislearning.service.UserService;
import com.example.redislearning.util.RedisKeyUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 用户管理服务实现类
 * 集成Redis缓存和分布式特性
 */
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private RedisTemplate<String, String> stringRedisTemplate;

    private static final Integer USER_CACHE_EXPIRE = 30; // 30分钟
    private static final Integer LEADERBOARD_EXPIRE = 5; // 5分钟

    /**
     * 用户注册
     * <p>
     * 处理新用户注册流程，包括：
     * 1. 参数校验
     * 2. 用户名和邮箱唯一性检查
     * 3. 密码加密处理
     * 4. 用户信息初始化
     * 5. 数据库保存
     * 6. Redis缓存和计数器初始化
     *
     * @param user 用户注册信息，必须包含username和password
     * @return 注册成功的用户信息，包含生成的用户ID
     * @throws IllegalArgumentException 当用户信息不完整时
     * @throws RuntimeException         当用户名已存在或邮箱已注册时
     */
    @Override
    @Transactional
    public User registerUser(User user) {
        // 参数校验
        if (user == null || user.getUsername() == null || user.getPassword() == null) {
            throw new IllegalArgumentException("用户信息不完整");
        }

        // 检查用户名是否存在
        if (isUsernameExists(user.getUsername())) {
            throw new RuntimeException("用户名已存在");
        }

        // 检查邮箱是否已注册
        if (user.getEmail() != null && isEmailExists(user.getEmail())) {
            throw new RuntimeException("邮箱已注册");
        }

        // 密码加密
        user.setPassword(encryptPassword(user.getPassword()));
        user.setCreatedAt(LocalDateTime.now());
        user.setUpdatedAt(LocalDateTime.now());
        user.setPoints(0);
        user.setTotalStudyTime(0);

        // 保存到数据库
        userMapper.insert(user);

        // 缓存用户信息
        cacheUserInfo(user);

        // 初始化Redis计数器
        initializeUserCounters(user.getId());

        return user;
    }

    /**
     * 用户登录
     * <p>
     * 处理用户登录认证，流程包括：
     * 1. 参数校验
     * 2. 优先从Redis缓存获取用户信息
     * 3. 缓存未命中时查询数据库
     * 4. 密码匹配验证
     * 5. 更新最后登录时间
     *
     * @param username 用户名
     * @param password 密码（明文）
     * @return 登录成功的用户信息
     * @throws IllegalArgumentException 当用户名或密码为空时
     * @throws RuntimeException         当用户名或密码错误时
     */
    @Override
    public User login(String username, String password) {
        if (username == null || password == null) {
            throw new IllegalArgumentException("用户名或密码不能为空");
        }

        // 先从缓存获取用户信息
        User user = getCachedUserByUsername(username);
        if (user == null) {
            // 缓存未命中，查询数据库
            user = userMapper.selectByUsername(username);
            if (user != null) {
                cacheUserInfo(user);
            }
        }

        if (user == null || !user.getPassword().equals(encryptPassword(password))) {
            throw new RuntimeException("用户名或密码错误");
        }

        // 更新最后登录时间
        user.setLastLoginTime(LocalDateTime.now());
        userMapper.updateLastLoginTime(user.getId(), user.getLastLoginTime());

        return user;
    }

    /**
     * 根据用户ID获取用户信息
     * <p>
     * 采用缓存优先策略：
     * 1. 首先从Redis缓存获取
     * 2. 缓存未命中时查询数据库
     * 3. 查询成功后写入缓存
     *
     * @param userId 用户ID
     * @return 用户信息，如果用户不存在返回null
     * @throws IllegalArgumentException 当用户ID为空时
     */
    @Override
    public User getUserById(Integer userId) {
        if (userId == null) {
            throw new IllegalArgumentException("用户ID不能为空");
        }

        // 先从缓存获取
        String key = RedisKeyUtil.getUserInfoKey(userId);
        User user = (User) redisTemplate.opsForValue().get(key);

        if (user == null) {
            // 缓存未命中，查询数据库
            user = userMapper.selectById(userId);
            if (user != null) {
                cacheUserInfo(user);
            }
        }

        return user;
    }

    /**
     * 更新用户信息
     * <p>
     * 处理用户信息更新，包括：
     * 1. 参数校验
     * 2. 用户存在性检查
     * 3. 邮箱唯一性检查（如更新邮箱）
     * 4. 密码加密（如更新密码）
     * 5. 数据库更新
     * 6. 缓存同步更新
     *
     * @param user 更新的用户信息，必须包含用户ID
     * @return 更新后的用户信息
     * @throws IllegalArgumentException 当用户信息不完整时
     * @throws RuntimeException         当用户不存在或邮箱已被使用时
     */
    @Override
    @Transactional
    public User updateUser(User user) {
        if (user == null || user.getId() == null) {
            throw new IllegalArgumentException("用户信息不完整");
        }

        // 检查用户是否存在
        User existingUser = getUserById(user.getId());
        if (existingUser == null) {
            throw new RuntimeException("用户不存在");
        }

        // 如果更新邮箱，检查邮箱是否已被使用
        if (user.getEmail() != null && !user.getEmail().equals(existingUser.getEmail())) {
            if (isEmailExists(user.getEmail())) {
                throw new RuntimeException("邮箱已被使用");
            }
        }

        // 如果更新密码，加密新密码
        if (user.getPassword() != null) {
            user.setPassword(encryptPassword(user.getPassword()));
        }

        user.setUpdatedAt(LocalDateTime.now());
        userMapper.updateById(user);

        // 更新缓存
        cacheUserInfo(user);

        return user;
    }

    /**
     * 删除用户
     * <p>
     * 彻底删除用户及其相关数据，包括：
     * 1. 删除数据库中的用户记录
     * 2. 清除Redis中的用户缓存
     * 3. 清除用户相关的Redis计数器
     * 4. 从各种排行榜中移除用户
     *
     * @param userId 要删除的用户ID
     * @return 删除成功返回true，失败返回false
     * @throws IllegalArgumentException 当用户ID为空时
     */
    @Override
    @Transactional
    public boolean deleteUser(Integer userId) {
        if (userId == null) {
            throw new IllegalArgumentException("用户ID不能为空");
        }

        // 删除数据库记录
        int result = userMapper.delete(userId);

        if (result > 0) {
            // 删除缓存
            String key = RedisKeyUtil.getUserInfoKey(userId);
            redisTemplate.delete(key);

            // 删除相关Redis计数器
            clearUserCounters(userId);

            // 从排行榜中移除
            removeFromLeaderboards(userId);

            return true;
        }

        return false;
    }

    /**
     * 获取用户积分
     * <p>
     * 采用缓存优先策略：
     * 1. 首先从Redis缓存获取用户积分
     * 2. 缓存未命中时查询数据库并同步到缓存
     * 3. 缓存有效期30分钟
     *
     * @param userId 用户ID
     * @return 用户当前积分，用户不存在时返回0
     * @throws IllegalArgumentException 当用户ID为空时
     */
    @Override
    public Integer getUserPoints(Integer userId) {
        if (userId == null) {
            throw new IllegalArgumentException("用户ID不能为空");
        }

        String key = RedisKeyUtil.getUserPointsKey(userId);
        String value = stringRedisTemplate.opsForValue().get(key);

        if (value != null) {
            return Integer.valueOf(value);
        }

        // Redis未命中，查询数据库
        User user = getUserById(userId);
        if (user != null) {
            Integer points = user.getPoints();
            stringRedisTemplate.opsForValue().set(key, String.valueOf(points), USER_CACHE_EXPIRE, TimeUnit.MINUTES);
            return points;
        }

        return 0;
    }

    /**
     * 增加用户积分
     * <p>
     * 原子性地为用户增加积分，包括：
     * 1. 更新数据库中的积分值
     * 2. 同步更新Redis中的积分计数器
     * 3. 更新积分排行榜
     * 4. 设置缓存过期时间为30分钟
     *
     * @param userId 用户ID
     * @param points 要增加的积分数，必须为正数
     * @return 增加后的最新积分值
     * @throws IllegalArgumentException 当参数为空时
     */
    @Override
    @Transactional
    public Integer incrementUserPoints(Integer userId, Integer points) {
        if (userId == null || points == null) {
            throw new IllegalArgumentException("参数不能为空");
        }

        // 更新数据库
        userMapper.incrementPoints(userId, points);

        // 更新Redis计数器
        String key = RedisKeyUtil.getUserPointsKey(userId);
        Long newPoints = stringRedisTemplate.opsForValue().increment(key, points);

        // 设置过期时间
        stringRedisTemplate.expire(key, USER_CACHE_EXPIRE, TimeUnit.MINUTES);

        // 更新排行榜
        updatePointsLeaderboard(userId, newPoints.intValue());

        return newPoints.intValue();
    }

    /**
     * 减少用户积分
     * <p>
     * 原子性地为用户减少积分，包括：
     * 1. 检查积分是否足够
     * 2. 调用incrementUserPoints方法执行减少操作
     * 3. 同步更新数据库和Redis
     * 4. 更新积分排行榜
     *
     * @param userId 用户ID
     * @param points 要减少的积分数，必须为正数
     * @return 减少后的最新积分值
     * @throws IllegalArgumentException 当参数无效时
     * @throws RuntimeException         当积分不足时
     */
    @Override
    @Transactional
    public Integer decrementUserPoints(Integer userId, Integer points) {
        if (userId == null || points == null || points < 0) {
            throw new IllegalArgumentException("参数无效");
        }

        // 检查积分是否足够
        Integer currentPoints = getUserPoints(userId);
        if (currentPoints < points) {
            throw new RuntimeException("积分不足");
        }

        return incrementUserPoints(userId, -points);
    }

    /**
     * 获取用户学习时长
     * <p>
     * 采用缓存优先策略获取用户的总学习时长：
     * 1. 首先从Redis缓存获取学习时长
     * 2. 缓存未命中时查询数据库并同步到缓存
     * 3. 缓存有效期30分钟
     *
     * @param userId 用户ID
     * @return 用户总学习时长（分钟），用户不存在时返回0
     * @throws IllegalArgumentException 当用户ID为空时
     */
    @Override
    public Integer getUserStudyDuration(Integer userId) {
        if (userId == null) {
            throw new IllegalArgumentException("用户ID不能为空");
        }

        String key = RedisKeyUtil.getUserStudyDurationKey(userId);
        String value = stringRedisTemplate.opsForValue().get(key);

        if (value != null) {
            return Integer.valueOf(value);
        }

        // Redis未命中，查询数据库
        User user = getUserById(userId);
        if (user != null) {
            Integer duration = user.getTotalStudyTime();
            stringRedisTemplate.opsForValue().set(key, String.valueOf(duration), USER_CACHE_EXPIRE, TimeUnit.MINUTES);
            return duration;
        }

        return 0;
    }

    /**
     * 增加用户学习时长
     * <p>
     * 原子性地为用户增加学习时长，包括：
     * 1. 更新数据库中的学习时长
     * 2. 同步更新Redis中的学习时长计数器
     * 3. 更新学习时长排行榜
     * 4. 设置缓存过期时间为30分钟
     *
     * @param userId          用户ID
     * @param durationMinutes 要增加的学习时长（分钟），必须为正数
     * @return 增加后的最新学习时长（分钟）
     * @throws IllegalArgumentException 当参数为空时
     */
    @Override
    @Transactional
    public Integer incrementStudyDuration(Integer userId, Integer durationMinutes) {
        if (userId == null || durationMinutes == null) {
            throw new IllegalArgumentException("参数不能为空");
        }

        // 更新数据库
        userMapper.incrementStudyDuration(userId, durationMinutes);

        // 更新Redis计数器
        String key = RedisKeyUtil.getUserStudyDurationKey(userId);
        Long newDuration = stringRedisTemplate.opsForValue().increment(key, durationMinutes);

        // 设置过期时间
        stringRedisTemplate.expire(key, USER_CACHE_EXPIRE, TimeUnit.MINUTES);

        // 更新学习时长排行榜
        updateStudyDurationLeaderboard(userId, newDuration.intValue());

        return newDuration.intValue();
    }

    /**
     * 获取用户排行榜
     * <p>
     * 根据指定类型获取用户排行榜，支持多种排序维度：
     * 1. "points" - 按积分排序
     * 2. "study_duration" - 按学习时长排序
     * 3. "courses_completed" - 按完成课程数排序
     * <p>
     * 采用Redis缓存优先策略，缓存未命中时从数据库查询
     *
     * @param type  排行榜类型，支持：points, study_duration, courses_completed
     * @param limit 返回排行榜的最大用户数，必须大于0
     * @return 排行榜列表，每个元素包含用户ID、用户名、分数和排名
     * @throws IllegalArgumentException 当类型不支持或limit无效时
     */
    @Override
    public List<Map<String, Object>> getUserLeaderboard(String type, int limit) {
        if (type == null || limit <= 0) {
            throw new IllegalArgumentException("参数无效");
        }

        String key;
        switch (type) {
            case "points":
                key = RedisKeyUtil.getPointsLeaderboardKey();
                break;
            case "study_duration":
                key = RedisKeyUtil.getStudyDurationLeaderboardKey();
                break;
            case "courses_completed":
                key = RedisKeyUtil.getCompletedCoursesLeaderboardKey();
                break;
            default:
                throw new IllegalArgumentException("不支持的排行榜类型");
        }

        // 从Redis获取排行榜
        Set<String> userIds = stringRedisTemplate.opsForZSet().reverseRange(key, 0, limit - 1);
        if (userIds == null || userIds.isEmpty()) {
            // Redis未命中，从数据库查询并构建排行榜
            return buildLeaderboardFromDatabase(type, limit);
        }

        return buildLeaderboardFromRedis(type, limit);
    }

    /**
     * 获取用户综合统计信息
     * <p>
     * 聚合用户的各类统计信息，包括：
     * 1. 基础用户信息（ID、用户名、邮箱、创建时间、最后登录时间）
     * 2. 实时积分和学习时长
     * 3. 数据库中的学习统计信息（课程完成数、学习天数等）
     *
     * @param userId 用户ID
     * @return 包含用户综合统计信息的Map，键包括：userId, username, email, createTime,
     * lastLoginTime, points, studyDuration, 以及数据库中的其他学习统计
     * @throws IllegalArgumentException 当用户ID为空时
     */
    @Override
    public Map<String, Object> getUserStatistics(Integer userId) {
        if (userId == null) {
            throw new IllegalArgumentException("用户ID不能为空");
        }

        Map<String, Object> statistics = new HashMap<>();

        User user = getUserById(userId);
        if (user != null) {
            statistics.put("userId", user.getId());
            statistics.put("username", user.getUsername());
            statistics.put("email", user.getEmail());
            statistics.put("createTime", user.getCreatedAt());
            statistics.put("lastLoginTime", user.getLastLoginTime());
        }

        // 获取实时统计
        statistics.put("points", getUserPoints(userId));
        statistics.put("studyDuration", getUserStudyDuration(userId));

        // 从数据库获取其他统计
        Map<String, Object> dbStats = userMapper.getLearningStats(userId);
        if (dbStats != null) {
            statistics.putAll(dbStats);
        }

        return statistics;
    }

    /**
     * 检查用户名是否已存在
     *
     * @param username 要检查的用户名
     * @return 如果用户名已存在返回true，否则返回false
     */
    @Override
    public boolean isUsernameExists(String username) {
        if (username == null) {
            return false;
        }
        return userMapper.countByUsername(username) > 0;
    }

    /**
     * 检查邮箱是否已注册
     *
     * @param email 要检查的邮箱地址
     * @return 如果邮箱已注册返回true，否则返回false
     */
    @Override
    public boolean isEmailExists(String email) {
        if (email == null) {
            return false;
        }
        return userMapper.countByEmail(email) > 0;
    }

    // 私有辅助方法

    /**
     * 验证用户信息的完整性
     * <p>
     * 检查用户对象及其必要字段是否为空或空白
     *
     * @param user 待验证的用户对象
     * @throws IllegalArgumentException 当用户信息不完整时抛出异常
     */
    private void validateUser(User user) {
        if (user == null) {
            throw new IllegalArgumentException("用户信息不能为空");
        }
        if (user.getUsername() == null || user.getUsername().trim().isEmpty()) {
            throw new IllegalArgumentException("用户名不能为空");
        }
        if (user.getEmail() == null || user.getEmail().trim().isEmpty()) {
            throw new IllegalArgumentException("邮箱不能为空");
        }
        if (user.getPassword() == null || user.getPassword().trim().isEmpty()) {
            throw new IllegalArgumentException("密码不能为空");
        }
    }

    /**
     * 验证用户ID的有效性
     * <p>
     * 检查用户ID是否为正整数
     *
     * @param userId 待验证的用户ID
     * @throws IllegalArgumentException 当用户ID无效时抛出异常
     */
    private void validateUserId(Integer userId) {
        if (userId == null || userId <= 0) {
            throw new IllegalArgumentException("用户ID不能为空且必须大于0");
        }
    }

    /**
     * 密码加密
     * <p>
     * 使用MD5算法对密码进行加密处理
     *
     * @param password 明文密码
     * @return MD5加密后的密码字符串
     */
    private String encryptPassword(String password) {
        return DigestUtils.md5DigestAsHex(password.getBytes());
    }

    /**
     * 缓存用户信息
     * <p>
     * 将用户信息缓存到Redis，设置30分钟过期时间
     *
     * @param user 要缓存的用户信息
     */
    private void cacheUserInfo(User user) {
        if (user != null && user.getId() != null) {
            String key = RedisKeyUtil.getUserInfoKey(user.getId());
            redisTemplate.opsForValue().set(key, user, USER_CACHE_EXPIRE, TimeUnit.MINUTES);
        }
    }

    /**
     * 根据用户名从缓存获取用户信息
     * <p>
     * 注意：由于用户名可能变化，此方法当前返回null
     * 实际应该建立用户名到用户ID的映射关系
     *
     * @param username 用户名
     * @return 用户信息，当前实现始终返回null
     */
    private User getCachedUserByUsername(String username) {
        // 由于用户名可能变化，这里简化处理，实际应该建立用户名到用户ID的映射
        return null;
    }

    /**
     * 初始化用户Redis计数器
     * <p>
     * 为新注册用户初始化积分和学习时长的Redis计数器
     * 设置初始值为0，过期时间为30分钟
     *
     * @param userId 用户ID
     */
    private void initializeUserCounters(Integer userId) {
        String pointsKey = RedisKeyUtil.getUserPointsKey(userId);
        String durationKey = RedisKeyUtil.getUserStudyDurationKey(userId);

        stringRedisTemplate.opsForValue().set(pointsKey, "0", USER_CACHE_EXPIRE, TimeUnit.MINUTES);
        stringRedisTemplate.opsForValue().set(durationKey, "0", USER_CACHE_EXPIRE, TimeUnit.MINUTES);
    }

    /**
     * 清除用户Redis计数器
     * <p>
     * 删除用户相关的积分和学习时长Redis键
     *
     * @param userId 用户ID
     */
    private void clearUserCounters(Integer userId) {
        stringRedisTemplate.delete(RedisKeyUtil.getUserPointsKey(userId));
        stringRedisTemplate.delete(RedisKeyUtil.getUserStudyDurationKey(userId));
    }

    /**
     * 从排行榜中移除用户
     * <p>
     * 将用户从积分排行榜和学习时长排行榜中移除
     *
     * @param userId 用户ID
     */
    private void removeFromLeaderboards(Integer userId) {
        String userIdStr = String.valueOf(userId);
        stringRedisTemplate.opsForZSet().remove(RedisKeyUtil.getPointsLeaderboardKey(), userIdStr);
        stringRedisTemplate.opsForZSet().remove(RedisKeyUtil.getStudyDurationLeaderboardKey(), userIdStr);
    }

    /**
     * 更新积分排行榜
     * <p>
     * 将用户的最新积分更新到积分排行榜，设置5分钟过期时间
     *
     * @param userId 用户ID
     * @param points 用户当前积分
     */
    private void updatePointsLeaderboard(Integer userId, Integer points) {
        String key = RedisKeyUtil.getPointsLeaderboardKey();
        stringRedisTemplate.opsForZSet().add(key, String.valueOf(userId), points);
        stringRedisTemplate.expire(key, LEADERBOARD_EXPIRE, TimeUnit.MINUTES);
    }

    /**
     * 更新学习时长排行榜
     * <p>
     * 将用户的最新学习时长更新到学习时长排行榜，设置5分钟过期时间
     *
     * @param userId   用户ID
     * @param duration 用户当前学习时长（分钟）
     */
    private void updateStudyDurationLeaderboard(Integer userId, Integer duration) {
        String key = RedisKeyUtil.getStudyDurationLeaderboardKey();
        stringRedisTemplate.opsForZSet().add(key, String.valueOf(userId), duration);
        stringRedisTemplate.expire(key, LEADERBOARD_EXPIRE, TimeUnit.MINUTES);
    }

    /**
     * 更新完成课程数排行榜
     * <p>
     * 将用户的最新完成课程数更新到完成课程数排行榜，设置5分钟过期时间
     *
     * @param userId           用户ID
     * @param coursesCompleted 用户当前完成课程数
     */
    private void updateCoursesCompletedLeaderboard(Integer userId, Integer coursesCompleted) {
        String key = RedisKeyUtil.getUserCompletedCoursesKey(userId);
        stringRedisTemplate.opsForZSet().add(key, String.valueOf(userId), coursesCompleted);
        stringRedisTemplate.expire(key, LEADERBOARD_EXPIRE, TimeUnit.MINUTES);
    }

    /**
     * 从数据库构建排行榜数据
     * <p>
     * 根据排行榜类型从数据库查询用户数据，构建排行榜列表
     * 支持积分、学习时长、完成课程数三种排行榜
     *
     * @param type  排行榜类型（points/study_duration/courses_completed）
     * @param limit 返回的用户数量限制
     * @return 排行榜数据列表，每个元素包含用户信息及对应排行榜数据
     */
    private List<Map<String, Object>> buildLeaderboardFromDatabase(String type, int limit) {
        List<Map<String, Object>> leaderboard = new ArrayList<>();

        switch (type) {
            case "points":
                List<User> topPointsUsers = userMapper.getPointsLeaderboard(limit);
                for (User user : topPointsUsers) {
                    Map<String, Object> entry = new HashMap<>();
                    entry.put("userId", user.getId());
                    entry.put("username", user.getUsername());
                    entry.put("points", user.getPoints());
                    leaderboard.add(entry);
                }
                break;

            case "study_duration":
                List<User> topDurationUsers = userMapper.getStudyDurationLeaderboard(limit);
                for (User user : topDurationUsers) {
                    Map<String, Object> entry = new HashMap<>();
                    entry.put("userId", user.getId());
                    entry.put("username", user.getUsername());
                    entry.put("studyDuration", user.getTotalStudyTime());
                    leaderboard.add(entry);
                }
                break;

            case "courses_completed":
                List<User> topCoursesUsers = userMapper.getCompletedCoursesLeaderboard(limit);
                for (User user : topCoursesUsers) {
                    Map<String, Object> entry = new HashMap<>();
                    entry.put("userId", user.getId());
                    entry.put("username", user.getUsername());
                    leaderboard.add(entry);
                }
                break;
        }

        // 同步到Redis
        String key = getLeaderboardKey(type);
        for (Map<String, Object> entry : leaderboard) {
            Integer userId = (Integer) entry.get("userId");
            Double score = ((Number) entry.get(type)).doubleValue();
            stringRedisTemplate.opsForZSet().add(key, String.valueOf(userId), score);
        }
        stringRedisTemplate.expire(key, LEADERBOARD_EXPIRE, TimeUnit.MINUTES);

        return leaderboard;
    }

    /**
     * 从Redis构建排行榜数据
     * <p>
     * 从Redis有序集合中获取排行榜数据，按分数从高到低排序
     * 如果Redis中没有数据，返回空列表
     *
     * @param type  排行榜类型（points/study_duration/courses_completed）
     * @param limit 返回的用户数量限制
     * @return 排行榜数据列表，每个元素包含用户信息及对应排行榜数据
     */
    private List<Map<String, Object>> buildLeaderboardFromRedis(String type, int limit) {
        Set<ZSetOperations.TypedTuple<String>> topUsers;
        String key = null;

        switch (type) {
            case "points":
                key = RedisKeyUtil.getPointsLeaderboardKey();
                break;
            case "study_duration":
                key = RedisKeyUtil.getStudyDurationLeaderboardKey();
                break;
            case "courses_completed":
                key = RedisKeyUtil.getCompletedCoursesLeaderboardKey();
                break;
        }

        if (key == null) {
            return new ArrayList<>();
        }

        topUsers = stringRedisTemplate.opsForZSet().reverseRangeWithScores(key, 0, limit - 1);

        if (topUsers == null || topUsers.isEmpty()) {
            return new ArrayList<>();
        }

        List<Map<String, Object>> leaderboard = new ArrayList<>();
        for (ZSetOperations.TypedTuple<String> tuple : topUsers) {
            String userIdStr = tuple.getValue();
            Double score = tuple.getScore();

            if (userIdStr != null && score != null) {
                Integer userId = Integer.parseInt(userIdStr);
                User user = getUserById(userId);

                if (user != null) {
                    Map<String, Object> entry = new HashMap<>();
                    entry.put("userId", userId);
                    entry.put("username", user.getUsername());

                    switch (type) {
                        case "points":
                            entry.put("points", score.intValue());
                            break;
                        case "study_duration":
                            entry.put("studyDuration", score.intValue());
                            break;
                        case "courses_completed":
                            entry.put("coursesCompleted", score.intValue());
                            break;
                    }

                    leaderboard.add(entry);
                }
            }
        }

        return leaderboard;
    }

    private String getLeaderboardKey(String type) {
        switch (type) {
            case "points":
                return RedisKeyUtil.getPointsLeaderboardKey();
            case "study_duration":
                return RedisKeyUtil.getStudyDurationLeaderboardKey();
            case "courses_completed":
                return RedisKeyUtil.getCompletedCoursesLeaderboardKey();
            default:
                throw new IllegalArgumentException("不支持的排行榜类型");
        }
    }
}